
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: IndexTest3.java 377 2010-03-11 20:50:18Z archie.cobbs $
 */

package org.dellroad.sidekar;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import org.dellroad.sidekar.annotation.SidekarEntity;
import org.dellroad.sidekar.annotation.SidekarField;
import org.dellroad.sidekar.annotation.SidekarIndex;
import org.dellroad.sidekar.annotation.SidekarSet;
import org.dellroad.sidekar.util.JumpIterator;
import org.dellroad.sidekar.util.JumpJoiner;
import org.dellroad.sidekar.util.VoidDatabaseAction;
import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;

public class IndexTest3 extends DatabaseTest {

    private Person[] people;

    @Test
    public void testIndex1() {

        // Populate
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                populateDB();
            }
        });

        // Verify
        this.db.performTransaction(null, new VoidDatabaseAction() {
            public void executeVoid() {
                verifyDB();
            }
        });
    }

    protected void populateDB() {
        people = new Person[] {
            null,
            createPerson("red"),        // index = 1, id = 1, friends = 4, 5
            createPerson("green"),      // index = 2, id = 2
            createPerson("red"),        // index = 3, id = 3, friends = 1, 2, 3, 4, 5, 6, 7
            createPerson("blue"),       // index = 4, id = 4
            createPerson("orange"),     // index = 5, id = 5, friends = 5, 6, 7
            createPerson(null),         // index = 6, id = 6, friends = 4, 6, 7
            createPerson(null),         // index = 7, id = 7, friends = 1, 2, 6, 7
        };
        addFriends(1, 4, 5);
        addFriends(3, 1, 2, 3, 4, 5, 6, 7);
        addFriends(5, 5, 6, 7);
        addFriends(6, 4, 6, 7);
        addFriends(7, 1, 2, 6, 7);
    }

    protected void verifyDB() {

        // Check iterators
        checkIteration(person().iterator(), 1, 2, 3, 4, 5, 6, 7);
        checkIteration(index("tag").iterator(), 4, 2, 5, 1, 3, 6, 7);
        checkIteration(index("tag").iterator("red"), 1, 3);
        checkIteration(index("tag").jumpIterator("red"), 1, 3);
        checkIteration(index("tag").jumpIterator((Object)null), 6, 7);
        checkIteration(index("tag").jumpIterator("fuschia"));
        checkIteration(index("tag").jumpIterator("redder"));
        checkIteration((JumpIterator<Person>)people[1].getFriends().iterator(), 4, 5);
        checkIteration((JumpIterator<Person>)people[2].getFriends().iterator());
        checkIteration((JumpIterator<Person>)people[3].getFriends().iterator(), 1, 2, 3, 4, 5, 6, 7);
        checkIteration((JumpIterator<Person>)people[4].getFriends().iterator());
        checkIteration((JumpIterator<Person>)people[5].getFriends().iterator(), 5, 6, 7);
        checkIteration((JumpIterator<Person>)people[6].getFriends().iterator(), 4, 6, 7);
        checkIteration((JumpIterator<Person>)people[7].getFriends().iterator(), 1, 2, 6, 7);

        // Check reverse iterators
        checkIteration(person().iterator().reverse(), 7, 6, 5, 4, 3, 2, 1);
        checkIteration(index("tag").jumpIterator("red").reverse(), 3, 1);
        checkIteration(index("tag").jumpIterator("red").reverse(), 3, 1);
        checkIteration(index("tag").jumpIterator((Object)null).reverse(), 7, 6);

        // Do some joins
        checkJoin(person().iterator(), (JumpIterator<Person>)people[1].getFriends().iterator(), 4, 5);
        checkJoin(person().iterator(), (JumpIterator<Person>)people[4].getFriends().iterator());
        checkJoin(person().iterator(), (JumpIterator<Person>)people[5].getFriends().iterator(), 5, 6, 7);
        checkJoin(index("tag").jumpIterator("red"), person().iterator(), 1, 3);
        checkJoin(index("tag").jumpIterator("red"), (JumpIterator<Person>)people[7].getFriends().iterator(), 1);
        checkJoin(
          (JumpIterator<Person>)people[5].getFriends().iterator(),
          (JumpIterator<Person>)people[6].getFriends().iterator(),
          (JumpIterator<Person>)people[7].getFriends().iterator(),
          6, 7);
        checkJoin(
          (JumpIterator<Person>)people[1].getFriends().iterator(),
          (JumpIterator<Person>)people[3].getFriends().iterator(),
          (JumpIterator<Person>)people[5].getFriends().iterator(),
          5);
        checkJoin(
          (JumpIterator<Person>)people[5].getFriends().iterator(),
          (JumpIterator<Person>)people[6].getFriends().iterator(),
          index("tag").jumpIterator((Object)null),
          6, 7);
        checkJoin(
          (JumpIterator<Person>)people[5].getFriends().iterator(),
          (JumpIterator<Person>)people[6].getFriends().iterator(),
          index("tag").jumpIterator("red"));
    }

    @SuppressWarnings("unchecked")
    private void checkJoin(JumpIterator<Person> jit1, JumpIterator<Person> jit2, int... indexes) {
        checkIteration(new JumpJoiner<Person>(Arrays.asList(jit1, jit2)), indexes);
    }

    @SuppressWarnings("unchecked")
    private void checkJoin(JumpIterator<Person> jit1, JumpIterator<Person> jit2, JumpIterator<Person> jit3, int... indexes) {
        checkIteration(new JumpJoiner<Person>(Arrays.asList(jit1, jit2, jit3)), indexes);
    }

    private void checkIteration(ListIterator<Person> i, int... indexes) {
        ArrayList<Person> actual = new ArrayList<Person>();
        while (i.hasNext())
            actual.add(i.next());
        ArrayList<Person> reversed = new ArrayList<Person>();
        while (i.hasPrevious())
            reversed.add(i.previous());
        ArrayList<Person> expected = new ArrayList<Person>();
        for (int index : indexes)
            expected.add(this.people[index]);
        assertEquals((Object)actual, (Object)expected);
        Collections.reverse(expected);
        assertEquals((Object)reversed, (Object)expected);
    }

    private Person createPerson(String tag) {
        Person person = person().create();
        person.setTag(tag);
        return person;
    }

    private void addFriends(int index, int... friends) {
        for (int friendex : friends)
            this.people[index].getFriends().add(this.people[friendex]);
    }

    private Entity<Person> person() {
        return this.db.getEntity(Person.class);
    }

    private Index<Person> index(String name) {
        return person().getIndex(name);
    }

    @Override
    protected List<Class<?>> getSchemaClasses() {
        return Arrays.<Class<?>>asList(Person.class);
    }

    // Model classes

    @SidekarEntity(indexes = {
      @SidekarIndex(name = "tag", fields = "tag")
    })
    public abstract static class Person implements Item {

        @SidekarField
        public abstract String getTag();
        public abstract void setTag(String tag);

        @SidekarSet
        public abstract Set<Person> getFriends();

        @Override
        public String toString() {
            return "Person[id=" + getSidekarId() + ",tag=" + getTag() + "]";
        }
    }
}

